package com.ffcs.trino.service;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ffcs.trino.config.TrinoJdbc;
import com.ffcs.trino.domin.ResponseData;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.Instant;
import java.util.*;
import java.util.stream.Stream;

/**
 * @author zhangzhengxing
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class TrinoService {

  private Instant lastCalledTime;

  /**
   * 文件存放目录
   */
  private final String directoryPath = "/etc/trino/catalog";

  /**
   * trino驱动
   */
  private final TrinoJdbc trinoJdbc;

  /**
   * 获取配置信息
   *
   * @return
   */
  public List<JSONObject> getConfig() {
    ObjectMapper objectMapper = new ObjectMapper();

    ArrayList<JSONObject> listData = new ArrayList<>();

    try (Stream<Path> paths = Files.walk(Paths.get(directoryPath))) {
      paths.filter(Files::isRegularFile).filter(path -> path.toString().endsWith(".properties")).forEach(path -> {
        Properties properties = new Properties();
        try {
          properties.load(new FileInputStream(path.toFile()));
          String json = objectMapper.writeValueAsString(properties);

          // 获取文件名称跟文件数据
          JSONObject entries = JSONUtil.parseObj(json);
          JSONObject file = new JSONObject();
          file.set(path.getFileName().toString(), entries);
          listData.add(file);

        } catch (IOException e) {
          log.error(e.getMessage());
        }
      });

    } catch (IOException e) {
      log.error(e.getMessage());
    }
    return listData;
  }

  /**
   * 写入配置文件
   *
   * @param config
   * @return
   */
  public Map<String, ResponseData> setConfig(List<JSONObject> config) {
    // 获取数据源状态
    Map<String, ResponseData> state = new HashMap<>(10);

    try {
      Instant now = Instant.now();
      if (lastCalledTime != null && now.isBefore(lastCalledTime.plusSeconds(60))) {
        throw new RuntimeException("当前接口60秒钟只能调用一次请不要频繁调用接口");
      }

      // 如果是空删除目录直接重启
      if (config.isEmpty()) {
        deleteFilesInDirectory(directoryPath);
//        executeSh();
        return null;
      }

      lastCalledTime = now;
      log.info("==========> 开始执行");

      deleteFilesInDirectory(directoryPath);
      createPropertiesFiles(config);
      executeSh();

      Thread.sleep(5000);

      // 启动检测
      JSONObject entries = config.get(0);
      if (ObjectUtil.isNotEmpty(entries)) {
        Set<String> strings = entries.keySet();
        // 获取strings第一个值
        String key = strings.iterator().next();
        String dataBase = key.split("\\.")[0];

        int i = 0;
        while (true) {
          String testConnection = trinoJdbc.getTestConnection(dataBase).getMsg();
          // 服务是否启动成功
          if (testConnection.contains("127.0.0.1:8080")) {
            log.info("连接结果 =======================> 服务请求中");
            if (i > 5) {
              throw new RuntimeException("服务未启动");
            }
            Thread.sleep(3000);
            i++;
          } else {
            break;
          }
        }

        while (true) {
          String testConnection = trinoJdbc.getTestConnection(dataBase).getMsg();
          if (testConnection.contains("initializing")) {
            log.info("等待Trino启动中...");
          } else {
            break;
          }
          Thread.sleep(2000);
        }
      }

      log.info("trino已启动中，开始数据库检测");

      // 连接所有的数据源用来获取状态
      config.forEach(item -> {
        Set<String> strings = item.keySet();

        // 获取数据源名称
        String key = strings.iterator().next();
        String dataBase = key.split("\\.")[0];

        // 获取请求结果
        ResponseData testConnection = trinoJdbc.getTestConnection(dataBase);
        log.info("连接结果 =======================> " + testConnection.getMsg());

        state.put(dataBase, testConnection);
      });

    } catch (InterruptedException e) {
      throw new RuntimeException(e.getMessage());
    }

    return state;
  }

  /**
   * 创建配置文件
   *
   * @param jsonObjects
   */
  public void createPropertiesFiles(List<JSONObject> jsonObjects) {
    for (JSONObject jsonObject : jsonObjects) {
      for (String key : jsonObject.keySet()) {
        Properties properties = new Properties();
        JSONObject propertiesJson = jsonObject.getJSONObject(key);
        for (String propertyKey : propertiesJson.keySet()) {
          properties.setProperty(propertyKey, propertiesJson.getStr(propertyKey));
        }
        try (Writer writer = new OutputStreamWriter(new FileOutputStream(directoryPath + "/" + key), "UTF-8")) {
          properties.store(writer, null);
        } catch (IOException e) {
          throw new RuntimeException(e.getMessage());
        }
      }
    }
  }

  /**
   * 删除目录文件
   *
   * @param directoryPath
   */
  public void deleteFilesInDirectory(String directoryPath) {
    Path dir = Paths.get(directoryPath);
    try {
      Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
          Files.delete(file);
          return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
          // 不要删除目录本身
          return FileVisitResult.CONTINUE;
        }
      });
    } catch (IOException e) {
      log.error(e.getMessage());
    }
  }

  /**
   * 执行脚本
   */
  public void executeSh() {
    try {
      // 杀死进程
      ProcessBuilder killProcessBuilder = new ProcessBuilder("sh", "-c", "kill -9 $(netstat -tulnp | grep :8080 | awk '{print $7}' | cut -d\"/\" -f1)");
      Process killProcess = killProcessBuilder.start();
      int killExitCode = killProcess.waitFor();
      if (killExitCode != 0) {
        System.out.println("杀死成功: " + killExitCode);
      }

      // 等待3秒钟等杀死进程
      Thread.sleep(3000);

      // 启动新进程
      ProcessBuilder processBuilder = new ProcessBuilder("sh", "-c", "nohup /usr/lib/trino/bin/run-trino > /data/log 2>&1 &");
      Process process = processBuilder.start();

      int exitCode = process.waitFor();
      if (exitCode == 0) {
        log.info("进程启动成功");
      } else {
        log.info("进程启动失败");
      }

    } catch (IOException | InterruptedException e) {
      log.error(e.getMessage());
    }
  }
}
